import type { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
import { ElLoading, ElMessage } from 'element-plus'
import type { LoadingInstance } from 'element-plus/lib/components/loading/src/loading'
import axios from 'axios'
import type { MyRequestConfig, MyExtraConfig, MyResult, MyResponse } from './type'
import useToken from '@/hooks/useToken'

// 默认是否开启请求loading
const defaultShowLoading = true

export default class MainRequest {
  loadingCount: number = 0 // 当前需要展示loading的请求发起数量
  instance: AxiosInstance // 请求实例
  loadingInstance?: LoadingInstance | null // loading实例
  constructor(config: MyRequestConfig) {
    this.instance = axios.create(config)
    // 若实例传入了拦截器则用传入的，否则使用默认的
    this.instance.interceptors.request.use(
      config.requestInterceptor?.bind(this) ?? this.defaultRequestInterceptor.bind(this),
      config.requestInterceptorCatch?.bind(this) ?? this.defaultRequestInterceptorCatch.bind(this)
    )
    this.instance.interceptors.response.use(
      config.responseInterceptor?.bind(this) ?? this.defaultResponseInterceptor.bind(this),
      config.responseInterceptorCatch?.bind(this) ?? this.defaultResponseInterceptorCatch.bind(this)
    )
  }
  /* 请求方法 */
  request(getConfig: MyRequestConfig): Promise<MyResponse<any, any>> {
    let headers: any = {}
    switch (getConfig.type) {
      case 'formData':
        headers['Content-Type'] = 'multipart/form-data'
        break
      case 'formUrl':
        headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'
        break
      default:
        headers['Content-Type'] = 'application/json;charset=UTF-8'
    }
    // 设置token
    const token = useToken().getToken()
    if (token && !getConfig.noToken) {
      headers.Authorization = 'Bearer ' + token
    }
    getConfig.headers = { ...getConfig.headers, ...headers }
    // 如果showLoading有传入值则用传入值，否则用默认值
    const showLoading = getConfig.showLoading ?? defaultShowLoading
    // 如果开启了请求loading，则进行loading逻辑判断
    if (showLoading) {
      if (this.loadingCount <= 0) {
        this.loadingCount = 1
        if (!this.loadingInstance) {
          this.loadingInstance = ElLoading.service({
            lock: true,
            text: '请求数据中...',
            background: 'rgba(0,0,0,0.5)'
          })
        }
      } else {
        this.loadingCount++
      }
    }
    const config = { ...getConfig }
    return new Promise((resolve, reject) => {
      this.instance
        .request(config)
        .then((res) => {
          resolve(res as MyResponse)
        })
        .catch((err) => {
          reject(err)
        })
        .finally(() => {
          // loading效果判断
          if (showLoading) {
            this.loadingCount--
            if (this.loadingInstance && this.loadingCount <= 0) {
              this.loadingInstance.close()
              this.loadingInstance = null
              this.loadingCount = 0
            }
          }
        })
    })
  }
  /**
   * @param url 请求地址
   * @param data 请求参数
   * @param config 请求配置
   * @returns
   */
  get(url: string, data?: any, config?: MyExtraConfig) {
    let myUrl = url
    if (data && typeof data === 'object') {
      let params = '?'
      Object.keys(data).forEach((key) => {
        if (data[key] !== undefined && data[key] !== null) {
          params += `&${key}=${data[key]}`
        }
      })
      params = params.replace('?&', '?')
      if (params.length > 1) {
        myUrl += params
      }
    }
    return this.request({ url: myUrl, method: 'GET', ...config })
  }
  /**
   * @param url 请求地址
   * @param data 请求参数
   * @param config 请求配置
   * @returns
   */
  post(url: string, data?: any, config?: MyExtraConfig) {
    return this.request({ url, data, method: 'POST', ...config })
  }
  /**
   * @param url 请求地址
   * @param data 请求参数
   * @param config 请求配置
   * @returns
   */
  delete(url: string, data?: any, config?: MyExtraConfig) {
    return this.request({ url, data, method: 'DELETE', ...config })
  }
  /**
   * @param url 请求地址
   * @param data 请求参数
   * @param config 请求配置
   * @returns
   */
  patch(url: string, data?: any, config?: MyExtraConfig) {
    return this.request({ url, data, method: 'PATCH', ...config })
  }
  /**
   * @param url 请求地址
   * @param data 请求参数
   * @param config 请求配置
   * @returns
   */
  put(url: string, data?: any, config?: MyExtraConfig) {
    return this.request({ url, data, method: 'PUT', ...config })
  }
  /**
   * 默认请求拦截
   */
  private defaultRequestInterceptor(config: InternalAxiosRequestConfig) {
    return config
  }
  /**
   * 默认请求出错拦截
   */
  private defaultRequestInterceptorCatch(err: any) {
    ElMessage.error('请求出错！', err)
    return err
  }

  /**
   * 默认响应拦截
   */
  private defaultResponseInterceptor(res: AxiosResponse) {
    if (!res.data.success) {
      ElMessage.warning(res.data.message || '请求数据失败')
    }
    return res
  }

  /**
   * 默认响应出错拦截
   */
  private defaultResponseInterceptorCatch(err: any) {
    const msg = err?.response?.data?.error_description ?? '数据响应出错！'
    ElMessage.error(msg)
    throw new Error(msg)
  }
}
